﻿using GuanQL.Core;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using ZhongJia.Core.Auditing;
using ZhongJia.Core.Extensions;
using ZhongJia.Core.Utility;

namespace GanQL.Web.Infrastructure.Filters
{
    public class AuditFilter : IAsyncActionFilter
    {
        public readonly ILogger _logger;
        public readonly IWebContext _webContext;
        public AuditFilter(
            IWebContext webContext,
            ILoggerFactory loggerFactory)
        {
            _webContext = webContext;
            _logger = loggerFactory.CreateLogger(nameof(AuditFilter));
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var auditInfo = CreateAuditInfo(context.ActionDescriptor.DisplayName, context.ActionArguments);
            var stopwatch = Stopwatch.StartNew();
            try
            {
                var result = await next();
                if (result.Exception != null && !result.ExceptionHandled)
                {
                    auditInfo.Exception = result.Exception;
                }
            }
            catch (Exception ex)
            {
                auditInfo.Exception = ex;
                throw;
            }
            finally
            {
                stopwatch.Stop();
                auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);

                if (auditInfo.Exception == null)
                {
                    _logger.LogInformation(auditInfo.ToString());
                }
                else
                {
                    _logger.LogWarning(auditInfo.ToString());
                }
            }
        }

        private AuditInfo CreateAuditInfo(string actionName, IDictionary<string, object> arguments)
        {
            var auditInfo = new AuditInfo
            {
                TenantId = _webContext.TenantId,
                UserId = _webContext.CurrentUser?.Id,
                MethodName = actionName,
                Parameters = ConvertArgumentsToJson(arguments),
                ExecutionTime = SystemClock.Now,
                ClientIpAddress = _webContext.ClientIpAddress
            };

            return auditInfo;
        }

        private string ConvertArgumentsToJson(IDictionary<string, object> arguments)
        {
            try
            {
                if (arguments.IsNullOrEmpty())
                {
                    return "{}";
                }

                var dictionary = new Dictionary<string, object>();

                foreach (var argument in arguments)
                {
                    if (argument.Value == null)
                    {
                        dictionary[argument.Key] = null;
                    }
                    else
                    {
                        dictionary[argument.Key] = argument.Value;
                    }
                }

                return JsonConvert.SerializeObject(dictionary);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex.ToString(), ex);
                return "{}";
            }
        }
    }
}

